package cate.game.role;

import cate.game.GameBody;
import cate.game.client.ClientContext;
import cate.game.common.msg.TimeSpanResp;
import cate.game.framework.ModuleMan;
import cate.game.framework.ThreadShare;
import cate.game.res.Pair;
import cate.game.role.base.RoleBase;
import cate.game.role.merge.RoleTraversal;
import cate.game.util.api.LoginApi;
import cate.game.util.api.StatApi;
import cp.solution.ops.status.ModOprStatus;
import cp.solution.util.time.TimeSpanType;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.Predicate;

/**
 * 角色模块管理
 */
public class RoleManager extends ModuleMan {

    public RoleTraversal traversal = new RoleTraversal();

    @Override
    public void initIndependent(GameBody game) throws Exception {
        super.initIndependent(game);
        traversal.initIndependent(game);
    }

    @Override
    public void initDependent() throws Exception {
        traversal.initDependent();
    }

    private final Map<String, ReentrantLock> lockMap = new ConcurrentHashMap<>();

    private Map<String, Role> activeMap = new ConcurrentHashMap<>();

    //<rolebase, activeTime>的缓存,不入库
    private final Map<String, Pair<RoleBase, Long>> roleBaseCache = new ConcurrentHashMap<>();

    public void removeFromMemory(String roleId) {
        this.lockMap.remove(roleId);
        this.activeMap.remove(roleId);
        this.roleBaseCache.remove(roleId);
    }

    public void putActive(Role role) {
        activeMap.put(role.getUid(), role);
        roleBaseCache.remove(role.getUid());
        role.getStatus().onEnter();
    }

    public Role getRoleFromActive(String roleId) {
        Role role = activeMap.get(roleId);
        if (role == null) {
            return null;
        }
        return active(role);
    }

    /**
     * 保持masterPO的活跃
     */
    protected Role active(Role role) {
        if (role != null && role.getCc() != null && role.getCc().getNetty() != null) {
            role.getCc().active();
        }
        return role;
    }

    /**
     * 根据名字获得主角，暂时先这么写
     *
     * @param roleName 主角名
     * @param num      数量
     * @return 列表
     */
    public List<Role> getRolesByName(String roleName, int num) {
        Page<RoleBase> roleBases = null;
        try {
            roleBases = game.facilities.getMongo().roleBase.findByName(roleName, Pageable.ofSize(num));
        } catch (Exception e) {
            log.error("根据名字获取主角信息失败", e);
        }
        List<Role> roles = new ArrayList<>();
        if (roleBases != null && !roleBases.isEmpty()) {
            for (RoleBase roleBase : roleBases) {
                roles.add(new Role(game, roleBase.uid));
            }
        }

        // test lab
        List<RoleBase> rbList = game.facilities.getMongo().roleBase.searchByName(roleName, num);
        for(RoleBase rb : rbList) {
            log.info("rb={}", rb);
        }
        //

        return roles;
    }

    private Role getRoleFromDB(String roleId, boolean readOnly) {
        Role role = new Role(game, roleId);
        RoleBase roleBase = role.getBase(false);
        if (roleBase == null) {
            return null;
        }
        updateOfflineRoleBase(role);
        if (!readOnly) {
            role.setCc(ClientContext.createOffline());
            putActive(role);
        }
        return role;
    }

    public Pair<RoleBase, Long> getRoleBase(String roleId) {
        return roleBaseCache.get(roleId);
    }

    public void updateOfflineRoleBase(Role role) {
        Pair<RoleBase, Long> pair = roleBaseCache.get(role.getUid());
        if (pair == null) {
            pair = new Pair<>(role.getBase().copy(), role.getStatus().activeTime);
            roleBaseCache.put(role.getUid(), pair);
        } else {
            pair.k = role.getBase().copy();
        }
    }

    public Role getRole(String roleId) {
        return getRole(roleId, false);
    }

    public Role getRole(String roleId, boolean readOnly) {
        if (StringUtils.isEmpty(roleId)) {
            return null;
        }
        Role role = null;
        ReentrantLock lock = getLock(roleId);
        lock.lock();
        try {
            role = getRoleFromActive(roleId);
            if (role == null) {
                role = getRoleFromDB(roleId, readOnly);
            }
        } catch (Exception e) {
            log.error("", e);
        } finally {
            lock.unlock();
//            lockMap.remove(roleId);
        }
        return role;
    }

    public void saveAndDrop(Role role) {
        saveAndDrop(role, true);
    }

    public ReentrantLock getLock(String roleUid) {
        ReentrantLock lock = lockMap.get(roleUid);
        if (lock == null) {
            synchronized (lockMap) {
                lock = lockMap.get(roleUid);
                if (lock == null) {
                    lock = new ReentrantLock();
                    lockMap.put(roleUid, lock);
                }
            }
        }
        return lock;
    }

    public void saveAndDrop(Role role, boolean removeActive) {
        if (role == null) {
            return;
        }
        ReentrantLock lock = getLock(role.getUid());
        boolean needRelease = false;
        if (!lock.isLocked()) {
            needRelease = true;
            lock.lock();
        }
        try {
            offline(role);
            save(role);
            if (removeActive) {
                activeMap.remove(role.getUid());
            }
        } catch (Exception e) {
            log.error("", e);
        } finally {
            if (needRelease) {
                lock.unlock();
//                lockMap.remove(role.getUid());
            }
        }
    }

    public void offline(Role role) {
        log.info("玩家{}离线了", role.getBase().name);
        role.getStatus().offlineSafe();
        role.getFriend().offlineSafe();
        role.getFarm().offlineSafe();
        role.getPvp().offlineSafe();
        role.getActivity().offlineSafe();
        role.getFight().offlineSafe();
        role.getRes().offlineSafe();
        role.getTrace().offlineSafe();
        //先这么写着
        role.getGame().crossRank.offline(role);
        //下线的时候更新rolebase缓存
        game.role.updateOfflineRoleBase(role);
        LoginApi.regToLogin(game, role);
        StatApi.updateRoleRecord(role);
        game.stat.whenOffline(role);
    }

    /** shutdown时候保存全部 */
    public void saveAll() {
        Iterator<Role> iter = activeMap.values().iterator();
        while (iter.hasNext()) {
            Role role = iter.next();
            if (role != null) {
                saveAndDrop(role);
            }
        }
        game.facilities.getMongoCache().flushAll();
    }

    private void save(Role role) {
        log.debug("保存角色 uid={}", role.getUid());
        role.save();
    }

    public int getActiveNum() {
        return activeMap.size();
    }

    public int getOnlineNum() {
        return (int) this.activeMap.values().stream().filter(r -> r.getStatus().isOnline()).count();
    }

    /**
     * 对活跃区的master做遍历
     *
     * @param breakMeet 遇到action的返回值是什么的时候就break;
     */
    public void activeForEach(Predicate<Role> action, boolean breakMeet) {
        Iterator<Role> iterator = this.activeMap.values().iterator();
        while (iterator.hasNext()) {
            boolean r = action.test(iterator.next());
            if (r == breakMeet) {
                break;
            }
        }
    }

    /**
     * 对活跃区的master做遍历
     */
    public void activeForEach(Consumer<Role> action) {
        Iterator<Role> iterator = this.activeMap.values().iterator();
        while (iterator.hasNext()) {
            action.accept(iterator.next());
        }
    }

    /**
     * 用自己的线程对活跃区的在线的master做遍历， 建议不涉及修改在线玩家数据的时候用
     */
    public void activeForOnlineSelfThread(Consumer<Role> action) {
        Iterator<Role> iterator = this.activeMap.values().iterator();
        while (iterator.hasNext()) {
            Role role = iterator.next();
            if (role != null && !role.entering && role.getStatus().isOnline()) {
                try {
                    action.accept(role);
                } catch (Exception e) {
                    log.error("active for online role {} error:", role.getBase().name, e);
                }
            }
        }
    }

    /**
     * 对活跃区的在线的master做遍历
     */
    public void activeForOnline(Consumer<Role> action) {
        Iterator<Role> iterator = this.activeMap.values().iterator();
        while (iterator.hasNext()) {
            Role role = iterator.next();
            if (role != null && !role.entering && role.getStatus().isOnline()) {
                try {
                    ThreadShare.instance().exe(role, () -> {
                        action.accept(role);
                    });
                } catch (Exception e) {
                    log.error("active for online role {} error:", role.getBase().name, e);
                }
            }
        }
    }

    /**
     * 是否在线玩家
     */
    public boolean activeForOnlineRole(String roleUid, Consumer<Role> action) {
        Role role = findOnline(roleUid);
        if(role == null){
            return false;
        }
        ThreadShare.instance().exe(role, () -> {
            action.accept(role);
        });
        return true;
    }

    /**
     * 查找在线用户的Master
     */
    public Role findActive(String masterIdHex) {
        if (masterIdHex == null) {
            return null;
        }
        return activeMap.get(masterIdHex);
    }

    /**
     * 查找在线用户的Master
     */
    public Role findOnline(String masterIdHex) {
        if (masterIdHex == null) {
            return null;
        }
        Role role = activeMap.get(masterIdHex);
        if (role == null || role.entering || !role.getStatus().isOnline()) {
            return null;
        }
        return role;
    }

    static final long SAVE_INTERVAL = 1000 * 60 * 3L;

    public int checkActive() {
        //一次最多保存50个人
        int index = 0;
        Iterator<Map.Entry<String, Role>> iter = this.activeMap.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<String, Role> en = iter.next();
            Role role = en.getValue();
            if (role != null) {
                final ClientContext cc = role.getCc();
                final ReentrantLock lock = getLock(role.getUid());
                boolean lockSuccess = false;
                try {
                    lockSuccess = lock.tryLock(100, TimeUnit.MILLISECONDS);
                    if(!lockSuccess){
                        continue;
                    }
                    if (cc != null && !cc.valid()) {
                        log.debug("{}将被超时移除", role.getBase().name);
                        saveAndDrop(role, false);
                        cc.kickOff(game);
                        iter.remove();
                        index++;
                    } else {
                        if (System.currentTimeMillis() - role.getStatus().lastSaveTime > SAVE_INTERVAL) {
                            role.getStatus().lastSaveTime = System.currentTimeMillis();
                            save(role);
                            log.debug("{}将被保存,cc={},cc.time={}", role.getBase().name, cc, cc != null ? new java.util.Date(cc.getActiveTime()) : null);
                            index++;
                        }
                    }
                } catch(InterruptedException ie){
                    log.error("trylock被超时打断", ie);
                    Thread.currentThread().interrupt();
                } catch (Exception e) {
                    log.error("试图让玩家下线的时候出错", e);
                } finally {
                    if(lockSuccess){
                        lock.unlock();
                    }
//                    lockMap.remove(role.getUid());
                }
            } else {
                iter.remove();
            }
        }
        return index;
    }

    @Override
    public void onDaySpan() {
        log.info("服务器{}开始跨天", game.config.srvId);
        activeForOnline(m -> {
            try {
                TimeSpanType type = m.getStatus().checkTimeSpan(TimeSpanType.DAY_SPAN, false);
                if (type != null) {
                    m.sendNow(new TimeSpanResp(TimeSpanType.DAY_SPAN));
                    m.onDaySpan(false);
                }
            } catch (Exception e) {
                log.error("对{}执行在线跨天处理时异常", m, e);
            }
        });
        roleBaseCache.clear();
    }

    public void onDaySpanForce() {
        log.info("服务器{}开始跨天", game.config.srvId);
        activeForOnline(m -> {
            try {
                TimeSpanType type = m.getStatus().checkTimeSpan(TimeSpanType.DAY_SPAN, true);
                if (type != null) {
                    m.sendNow(new TimeSpanResp(TimeSpanType.DAY_SPAN));
                    m.onDaySpan(false);
                }
            } catch (Exception e) {
                log.error("对{}执行在线跨天处理时异常", m, e);
            }
        });
        roleBaseCache.clear();
    }

    @Override
    public void onWeekSpan() {
        log.info("服务器{}开始跨周", game.config.srvId);
        activeForOnline(m -> {
            try {
                TimeSpanType type = m.getStatus().checkTimeSpan(TimeSpanType.WEEK_SPAN, false);
                if (type != null) {
                    m.sendNow(new TimeSpanResp(TimeSpanType.WEEK_SPAN));
                    m.onWeekSpan(false);
                }
            } catch (Exception e) {
                log.error("对{}执行在线跨周处理时异常", m, e);
            }
        });
    }

    public void onWeekSpanForce(){
        log.info("服务器{}开始跨周", game.config.srvId);
        activeForOnline(m -> {
            try {
                TimeSpanType type = m.getStatus().checkTimeSpan(TimeSpanType.WEEK_SPAN, true);
                if (type != null) {
                    m.sendNow(new TimeSpanResp(TimeSpanType.WEEK_SPAN));
                    m.onWeekSpan(false);
                }
            } catch (Exception e) {
                log.error("对{}执行在线跨周处理时异常", m, e);
            }
        });
    }

    @Override
    public void onMonthSpan() {
        log.info("服务器{}开始跨月", game.config.srvId);
        activeForOnline(m -> {
            try {
                TimeSpanType type = m.getStatus().checkTimeSpan(TimeSpanType.MONTH_SPAN, false);
                if (type != null) {
                    m.sendNow(new TimeSpanResp(TimeSpanType.MONTH_SPAN));
                    m.onMonthSpan(false);
                }
            } catch (Exception e) {
                log.error("对{}执行在线跨月处理时异常", m, e);
            }
        });
    }

    public void onMonthSpanForce() {
        log.info("服务器{}开始跨月", game.config.srvId);
        activeForOnline(m -> {
            try {
                TimeSpanType type = m.getStatus().checkTimeSpan(TimeSpanType.MONTH_SPAN, true);
                if (type != null) {
                    m.sendNow(new TimeSpanResp(TimeSpanType.MONTH_SPAN));
                    m.onMonthSpan(false);
                }
            } catch (Exception e) {
                log.error("对{}执行在线跨月处理时异常", m, e);
            }
        });
    }

    public List<Role> getActiveRoles() {
        return new ArrayList<>(activeMap.values());
    }

    @Override
    public ModOprStatus handleMergeSettle() {
        ModOprStatus mos = new ModOprStatus("角色管理器");
        mos.exe(() -> {
            mos.setLogs("目前角色管理模块本身没有要处理的逻辑");
        });
        return mos;
    }
}
